import { GetTiandituConfig } from './basemaps/tianditu';
import { GetLocalPngTilesConfig } from './basemaps/localPngTiles';
import { GetLocalMbTilesConfig } from './basemaps/localMbTiles';
import { IMapSource } from './basemaps/mapTypes';
import mapboxgl, {
  ExpressionSpecification,
  MapMouseEventType,
  StyleSpecification,
} from 'mapbox-gl';
import { MapboxToken } from '../shared/config';
import { ZoomManager } from '../features/zoom';
import {
  SourceLoadingManager,
  IDataLoadingStrategy,
  ILoadingContext,
} from './sourceLoading';
import logger from 'loglevel';

const MapboxAccessToken = MapboxToken;
mapboxgl.accessToken = MapboxAccessToken;

export type TMapSourceConfig =
  | {
      type: 'localPng';
      tilesPath?: string; // your-path/{z}/{x}/{y}.png
    }
  | {
      type: 'localMbTiles' | 'tianditu';
    };

const MapSource: Record<
  'tianditu' | 'localPng' | 'localMbTiles',
  (args: any) => IMapSource
> = {
  tianditu: GetTiandituConfig,
  localPng: GetLocalPngTilesConfig,
  localMbTiles: GetLocalMbTilesConfig,
};

export type Interaction = {
  type: MapMouseEventType;
  filter?: ExpressionSpecification;
  layers?: string[];
  handler: (event: any) => boolean;
};

/**
 * 核心底图类，负责地图的加载、资源管理和交互处理
 */
export class MapCore {
  map: mapboxgl.Map;
  mapLoaded: boolean = false;
  zoomManager!: ZoomManager;
  imagesLoadedSet = new Set<string>();

  /**
   * 数据加载管理器 - 负责策略选择和数据加载优化
   */
  private sourceLoadingManager: SourceLoadingManager =
    new SourceLoadingManager();

  /**
   * MapboxGL 导航控件（包含缩放按钮）
   * @private
   */
  private navigationControl: mapboxgl.NavigationControl;

  /**
   * 是否已添加导航控件
   * @private
   */
  private navigationControlAdded: boolean = false;

  constructor(container: string | HTMLElement, mapSource: TMapSourceConfig) {
    const map = (this.map = this.loadMap(container, mapSource));

    // 提前创建导航控件（包含缩放按钮）但不立即添加
    this.navigationControl = new mapboxgl.NavigationControl({
      showCompass: true, // 显示指南针
      showZoom: true, // 显示缩放按钮
      visualizePitch: true, // 可视化俯仰角
    });

    // 地图加载完成后初始化导航管理器和拉框缩放处理器
    this.zoomManager = new ZoomManager(map);
  }

  private loadMap(
    container: string | HTMLElement,
    mapSource: TMapSourceConfig,
  ): mapboxgl.Map {
    const getPreConfig = MapSource[mapSource.type];
    if (!getPreConfig) {
      throw new Error(`地图源 ${mapSource} 不存在`);
    }
    const preConfig = getPreConfig(mapSource);
    const { style, onLoad } = preConfig;
    const map = new mapboxgl.Map({
      container,
      style: style as StyleSpecification,
      // center: [106.22491803008376, 38.48376285544373], // 银川
      center: [116.98813361885675, 36.66121595740897], // 济南
      zoom: 13,
    });
    map.on('load', () => {
      this.map.setFog({});
      try {
        // load base map layers
        onLoad(map);
      } catch (error) {
        console.error(error);
        throw new Error('加载底图失败');
      }
      this.mapLoaded = true;
    });
    map.on('error', (e) => {
      // 忽略瓦片加载失败错误
      if (
        e.error &&
        e.error.message &&
        e.error.message.includes(
          'Could not load image because of The source image could not be decoded',
        )
      ) {
        return;
      }
      console.error('Mapbox GL Error:', e);
    });
    return map;
  }

  onceMapLoaded(callback: () => void): void {
    if (this.mapLoaded) {
      callback();
    } else {
      this.map.once('load', callback);
    }
  }

  /**
   * 显示地图导航控件（包含缩放按钮）
   * @param position 控件位置，默认为右上角
   */
  showNavigationControl(
    position:
      | 'top-right'
      | 'top-left'
      | 'bottom-right'
      | 'bottom-left' = 'top-right',
  ): void {
    if (!this.navigationControlAdded) {
      // 检查地图是否已加载
      if (this.mapLoaded) {
        this.map.addControl(this.navigationControl, position);
        this.navigationControlAdded = true;
      } else {
        // 如果地图尚未加载，等待加载完成后再添加控件
        this.map.once('load', () => {
          this.map.addControl(this.navigationControl, position);
          this.navigationControlAdded = true;
        });
      }
    }
  }

  /**
   * 隐藏地图导航控件
   */
  hideNavigationControl(): void {
    if (this.navigationControlAdded) {
      // 只有在地图已加载的情况下才尝试移除控件
      if (this.mapLoaded) {
        this.map.removeControl(this.navigationControl);
        this.navigationControlAdded = false;
      } else {
        // 如果地图尚未加载，标记为不添加，这样当地图加载后也不会添加
        this.navigationControlAdded = false;
      }
    }
  }

  /**
   * 设置地图缩放等级
   * @param zoom 缩放等级
   */
  setZoom(zoom: number): void {
    if (!this.mapLoaded) {
      console.warn('地图尚未加载完成，设置缩放级别可能不会立即生效');
      this.map.once('load', () => {
        this.map.setZoom(zoom);
      });
    } else {
      this.map.setZoom(zoom);
    }
  }

  setLayerTop(layerId: string) {
    if (this.map.getLayer(layerId)) {
      this.map.moveLayer(layerId);
    } else {
      console.warn(`图层 ${layerId} 不存在，无法设置为顶层`);
    }
  }

  // 交互管理
  /**
   *
   * @param id
   * @param interaction 此处 interaction 需要是 mapboxgl.Interaction 类
   */
  addInteraction(id: string, interaction: Interaction) {
    return this.map.addInteraction(id, interaction);
  }

  private addSource(
    sourceId: string,
    source: mapboxgl.SourceSpecification,
  ): Promise<string> {
    const waitLoad = new Promise<string>((resolve) => {
      if (this.mapLoaded) {
        this.map.addSource(sourceId, source);
        resolve(sourceId);
      } else {
        this.map.on('load', () => {
          this.map.addSource(sourceId, source);
          resolve(sourceId);
        });
      }
    });
    return waitLoad;
  }

  updateSource(sourceId: string, data: GeoJSON.GeoJSON) {
    const source = this.map.getSource(sourceId) as mapboxgl.GeoJSONSource;
    if (!source) {
      console.warn(`Source with ID ${sourceId} does not exist.`);
    }
    return source.setData(data);
  }

  updateSourceWithProperties(
    sourceId: string,
    update: { id: string; properties: Record<string, any> }[],
  ) {
    const source = this.map.getSource(sourceId) as mapboxgl.GeoJSONSource;
    if (!source) {
      console.warn(`Source with ID ${sourceId} does not exist.`);
      return;
    }
    // 更新数据源的属性
    const data = source._data as GeoJSON.FeatureCollection;
    const updateMap = update.reduce(
      (acc, item) => {
        acc[item.id] = item.properties;
        return acc;
      },
      {} as Record<string, Record<string, any>>,
    );
    data.features.forEach((feature) => {
      if (feature.id && updateMap[feature.id]) {
        feature.properties = Object.assign(
          feature.properties || {},
          updateMap[feature.id],
        );
        Reflect.deleteProperty(updateMap, feature.id); // 删除已更新的项
      }
    });
    if (Object.keys(updateMap).length > 0) {
      logger.warn(
        `以下 ID 的数据未找到对应的 feature 进行更新: ${Object.keys(updateMap).join(', ')}`,
      );
    }
    return source.setData(data);
  }

  /**
   * 添加或更新地图数据源， 此操作会自动更新渲染效果
   */
  async addOrUpdateSource(
    sourceId: string,
    data: mapboxgl.GeoJSONSourceSpecification,
  ): Promise<string> {
    const source = this.map.getSource(sourceId) as mapboxgl.GeoJSONSource;
    if (source) {
      console.time(`更新source ${sourceId}`);
      try {
        source.setData(data.data as GeoJSON.GeoJSON);
        return source.id;
      } finally {
        console.timeEnd(`更新source ${sourceId}`);
      }
    } else {
      console.time(`新增source ${sourceId}`);
      try {
        return await this.addSource(sourceId, data);
      } finally {
        console.timeEnd(`新增source ${sourceId}`);
      }
    }
  }

  /**
   * 使用策略模式添加或更新地图数据源（推荐）
   * 自动选择最优的数据加载策略
   */
  async addOrUpdateSourceWithStrategy(
    sourceId: string,
    data: mapboxgl.GeoJSONSourceSpecification,
    context?: ILoadingContext,
  ): Promise<string> {
    return this.sourceLoadingManager.loadData(sourceId, data, this, context);
  }

  /**
   * 获取数据加载管理器，用于注册自定义策略
   */
  getDataLoadingManager(): SourceLoadingManager {
    return this.sourceLoadingManager;
  }

  /**
   * 注册自定义数据加载策略
   */
  registerLoadingStrategy(strategy: IDataLoadingStrategy): void {
    this.sourceLoadingManager.registerStrategy(strategy);
  }

  // region 资源管理
  private loadImagePromise(
    map: mapboxgl.Map,
    url: string,
  ): Promise<ImageBitmap | HTMLImageElement | ImageData> {
    return new Promise((resolve, reject) => {
      map.loadImage(url, (error, image) => {
        if (error) {
          reject(error);
        } else if (!image) {
          reject(new Error('image not found'));
        } else {
          resolve(image);
        }
      });
    });
  }

  loadImages(images: Record<string, string>) {
    const entries = Object.entries(images);
    return Promise.all(
      entries.map(async ([name, url]) => {
        if (this.imagesLoadedSet.has(name)) {
          return;
        }
        const image = await this.loadImagePromise(this.map, url);
        this.map.addImage(name, image);
        this.imagesLoadedSet.add(name);
      }),
    );
  }

  // endregion 资源管理
}
